/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "vm.h"
#include "typedef.h"
#include "sfc_norflash_api.h"
#include "ioctl_cmds.h"
#include "list.h"

#define LOG_TAG_CONST     VM
#define LOG_TAG     "[VM]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"

#define VM_DEBUG_PRINTF     0
#if (VM_DEBUG_PRINTF == 1)
#define vm_info_log                     printf
#define vm_err_log(format, ...) printf("VM_ERR: fun:%s: line:%d\t" format, __FUNCTION__, __LINE__, ## __VA_ARGS__)
#define vm_puts                         printf
#else
#define vm_info_log(...)
#define vm_err_log(...)
#define vm_puts(...)
#endif

#define AT_RAM      sec(.volatile_ram_code)
#define AT_SPI_CODE      sec(.volatile_ram_code)

#if defined (VM_ITEM_MAX_NUM)
#define VM_INDEX_DES_MAX        (VM_ITEM_MAX_NUM)      // 12bit
#else
#define VM_INDEX_DES_MAX        (128)      // 12bit
#endif

#define VM_BLOCK_SIZE           (64 * 1024)
#define VM_SECTOR_SIZE          (4 * 1024)
#define VM_PAGE_SIZE            (256)

#define VM_PAGE_ALIGN_BACKWARD(ptr)     (u8 *)((u32)(ptr) & ~(VM_PAGE_SIZE - 1))
#define VM_SECTOR_ALIGN_BACKWARD(ptr)   (u8 *)((u32)(ptr) & ~(VM_SECTOR_SIZE - 1))
#define VM_BLOCK_ALIGN_BACKWARD(ptr)    (u8 *)((u32)(ptr) & ~(VM_BLOCK_SIZE - 1))

#define SPI_CACHE_READ_ADDR(addr)  (void *)(flash_addr2cpu_addr(addr))

typedef enum _FLASH_ERASER {
    CHIP_ERASER = 0,
    BLOCK_ERASER,
    SECTOR_ERASER,
    PAGE_ERASER,
} FLASH_ERASER;


#include "vm_commom.c"
#include "vm_printf.c"
#include "vm_sfc.c"

#include "vm_cfgfile.c"

// @note   config define

#define VM_WRITE_RAM_WHEN_FULL          0
#define VM_PROTECT_CHECK_EN             0

#define VM_MAX_PAGE_SIZE                     (16 * VM_PAGE_SIZE * 2)  // VM最大支持双64K
#define VM_MAX_SECTOR_SIZE                   (16 * VM_SECTOR_SIZE * 2) // VM最大支持双64K

#define VM_DEFRAG_SIZE                  (80) // VM 碎片整理。百分比

#define LABEL_INDEX_LEN_CRC_SIZE        (4)

/* -------------------vm_new_version------------------------ */

typedef struct _VM_DESC {
    u16 v_addr;
} VM_DESC;

typedef struct _AREA_RANGE {
    u32 addr;
    u32 len;            // byte
    u16 erase_bit_map;  // surport 16 * 4 sector
} AREA_INFO;

typedef struct __PROTECT_AREA {
    u32 addr;
    u32 len;
} PROTECT_AREA;

typedef struct _VM_STRUCT {
    AREA_INFO area[2];
    VM_DESC idx_desc[VM_INDEX_DES_MAX];

    int mutex;

#if VM_PROTECT_CHECK_EN
    PROTECT_AREA vm_protect;
#endif

#if VM_WRITE_RAM_WHEN_FULL
    struct list_head write_list;
#endif

    u32 last_write_addr;
    u8 warning_line;    // percentage
    u8 init_flag : 1;       // vm_init_statu
    u8 using_area : 1;      // 0:main_area   1:temp_area
    u8 page_flag : 1;       // page erase en
} VM_STRUCT;

typedef struct _VM_WRITE_RAM_LIST {
    // --write_list
    struct list_head list;

    // --vm_data_package
    u8 magic_unit[LABEL_INDEX_LEN_CRC_SIZE];
    u16 len;    // data len
    u8 data[0]; // data_len define by vm_unit_magic

} VM_WRITE_RAM_LIST;

// VM_MAGIC, this flag change from 1 to 0
#define VM_MAGIC_SIZE           (sizeof(u32))           // 4 byte len
#define VM_MAGIC_UNUSED         ((u32)0xFFFFFFFF)   // 1111-1111-1111-1111
#define VM_MAGIC_DEFRAGGING     ((u32)0xDDEEBB77)   // 1101-1110-1011-0111
#define VM_MAGIC_DEFRAG_OVER    ((u32)0xDDEEAA55)   // 1101-1110-1010-0101
#define VM_MAGIC_USING          ((u32)0x55AAAA55)   // 0101-1010-1010-0101
#define VM_MAGIC_WAIT_ERASE     ((u32)0x00000000)   // 0000-0000-0000-0000

#define READ_VM_8(buffer, pos) (((u8) buffer[pos]))
#define READ_VM_16(buffer, pos) (((u16) buffer[pos]) | (((u16)buffer[pos + 1]) << 8))
#define READ_VM_24(buffer, pos) (((u32) buffer[pos]) | (((u32)buffer[pos + 1]) << 8) | (((u32)buffer[pos + 2]) << 16))
#define READ_VM_32(buffer, pos) (((u32) buffer[pos]) | (((u32)buffer[pos + 1]) << 8) | \
    (((u32)buffer[pos + 2]) << 16) | (((u32) buffer[pos + 3])) << 24)

/*------------------------------------------------------------------
  @note   g_values
 */

static VM_STRUCT vm_obj;

#define VM_INIT_FLAG                (vm_obj.init_flag)
#define VM_PAGE_MODE                (vm_obj.page_flag)

#define VM_MUTEX                    (&vm_obj.mutex)
#define VM_LINE                     (vm_obj.warning_line)

#define VM_WRITE_LIST               (vm_obj.write_list)

#define VM_AREA_USING               (vm_obj.using_area)
#define VM_AREA_UNSUED              (!vm_obj.using_area)

#define VM_IDX_ADR(x)               (vm_obj.idx_desc[x].v_addr)

#define VM_AREA_ADR(x)              (vm_obj.area[x].addr)
#define VM_AREA_LEN(x)              (vm_obj.area[x].len)

#define VM_AREA_ERASE_INFO(x)       (vm_obj.area[x].erase_bit_map)

#define VM_LAST_ADR                 (vm_obj.last_write_addr)

/*
   |---0-7---|---8-19--|--20-31--|---len---|
   | _vm_crc |_vm_index| _vm_len |_vm_data |...
 */

typedef u8 _vm_crc;
typedef u16 _vm_len;
typedef u16 _vm_idx;

#define VM_MAX_DATA_LEN         (_vm_len)0x0FFF
#define VM_MAX_INDEX_NUM        (_vm_idx)0x0FFF

#define VM_UNIT_CRC(buffer)     ((_vm_crc)(READ_VM_8(buffer, 0)))
#define VM_UNIT_IDX(buffer)     ((_vm_idx)(READ_VM_16(buffer, 1)) & VM_MAX_INDEX_NUM)
#define VM_UNIT_LEN(buffer)     ((_vm_len)(READ_VM_16(buffer, 2) >> 4) & VM_MAX_DATA_LEN)

/*------------------------------------------------------------------
  @note   function define
 */
/*-----------------------------------------------------------------*/
static VM_ERR set_protect_info(u32 addr, u32 len);
static VM_ERR vm_reset(void);

VM_ERR vm_status(VM_STRUCT *vm_str_p);

BOOL sfc_erase(FLASH_ERASER cmd, u32 addr);
u32 sfc_write(const u8 *buf, u32 addr, u32 len);
u32 sfc_dma_write(const u8 *buf, u32 addr, u32 len);
u32 sfc_read(u8 *buf, u32 addr, u32 len);

s32 vm_read(vm_hdl hdl, u8 *data_buf, u16 len);
s32 _vm_write(vm_hdl hdl, u8 *data_buf, u16 len, u8 dma_mode);
void vm_update_recover(void);
extern const int support_vm_data_keep;

u32 sdfile_get_disk_capacity(void);
/*------------------------------------------------------------------
  @note   function API
 */
#define vm_mutex_init()   os_mutex_create(VM_MUTEX)

volatile static u8 vm_enter_critical = 0;        // 1: vm busy
static void vm_mutex_enter(void)
{
    local_irq_disable();
    vm_enter_critical = 1;
    local_irq_enable();
    os_mutex_pend(VM_MUTEX, 0);
}

static void vm_mutex_exit(void)
{
    local_irq_disable();
    vm_enter_critical = 0;
    local_irq_enable();
    os_mutex_post(VM_MUTEX);
}

u8 get_vm_statu(void)
{
    return vm_enter_critical;
}

/**@brief  VM初始化检测
  @param  void
  @return TURE: 已经初始化，FALSE：还没初始化
  @note   static u8 vm_init_check(void)
 */
static u8 vm_init_check(void)
{
    if (VM_INIT_FLAG) {
        return TRUE;
    } else {
        vm_err_log("err:vm_NOT_INIT\n");
        return FALSE;
    }
}

/**@brief  VM句柄检测
  @param  void
  @return TURE: 句柄可用，FALSE：句柄超出范围
  @note   static u8 vm_check_hdl(vm_hdl hdl)
 */
static u8 vm_check_hdl(vm_hdl hdl)
{
    if (hdl < VM_INDEX_DES_MAX) {
        return TRUE;
    } else {
        vm_err_log("err:vm index %d is overflow,max is %d\n", hdl, VM_INDEX_DES_MAX - 1);
        return FALSE;
    }
}

/**@brief  擦除VM区域函数
  @param  vm_area_p：VM区域
  @return none
  @note   static void _vm_area_erase(AREA_INFO * vm_area_p)
 */
// wait_ok:0 only send erase cmd, 1:waite till cmd finish
static BOOL _vm_area_erase(AREA_INFO *vm_area_p)
{
    u32 erase_addr = vm_area_p->addr;
    u16 num;
    u16 cnt;

    if (VM_PAGE_MODE) {
        num = vm_area_p->len / VM_PAGE_SIZE;
        for (cnt = 0; cnt < num; cnt++) {
            if (sfc_erase(PAGE_ERASER, erase_addr) == false) {
                vm_err_log("vm_erase_err\n");
                return false;
            }
            erase_addr += VM_PAGE_SIZE;
        }
    } else {
        num = vm_area_p->len / VM_SECTOR_SIZE;
        for (cnt = 0; cnt < num; cnt++) {
            if (sfc_erase(SECTOR_ERASER, erase_addr) == false) {
                vm_err_log("vm_erase_err\n");
                return false;
            }
            erase_addr += VM_SECTOR_SIZE;
        }
    }

    vm_area_p->erase_bit_map = 0;
    return true;
}

/**@brief  VM写数据临时链表检测函数
  @param  hdl：要检测链表中是否有对应句柄的数据
  @return NULL：无有效数据，NOT_NULL：链表入口
  @note   VM_WRITE_RAM_LIST * vm_write_ram_list_check(vm_hdl hdl)
 */
VM_WRITE_RAM_LIST *vm_write_ram_list_check(vm_hdl hdl)
{
#if VM_WRITE_RAM_WHEN_FULL
    VM_WRITE_RAM_LIST *ram_space;

    if (list_empty(&VM_WRITE_LIST)) {
        return NULL;
    }

    list_for_each_entry(ram_space, &VM_WRITE_LIST, list) {
        if (hdl == VM_UNIT_IDX(ram_space->magic_unit)) {
            return ram_space;
        }
    }
#endif
    return NULL;
}

/**@brief  VM数据拷贝函数
  @param  dst_addr：目标地址（相对flash地址）。src_addr：源地址（相对flash地址）
  @return void
  @note   static void vm_data_copy(u32 dst_addr, u32 src_addr, u32 len)
 */
static void vm_data_copy(u32 dst_addr, u32 src_addr, u32 len)
{
    u32 t_len;
    u8 *buf = malloc(VM_PAGE_SIZE);

    if (buf) {
        while (1) {
            t_len = len > VM_PAGE_SIZE ? VM_PAGE_SIZE : len;
            if (t_len == 0) {
                break;
            }

            sfc_read(buf, src_addr, t_len);
            sfc_write(buf, dst_addr, t_len);
            len -= t_len;
            dst_addr += t_len;
            src_addr += t_len;
        }
        free(buf);
    }
}

/**@brief  VM区域扫描函数，主要查找出最后写地址和各个index的最后更新地址
  @param  vm_area_p：要扫描的区域，此区域必须成为当前使用区域
  @return 最后更新地址。
  @note   static u32 vm_renew_all_index_last_updated_addr(AREA_INFO * vm_area_p)
 */
static u32 vm_renew_all_index_last_updated_addr(AREA_INFO *vm_area_p)
{
    u8 data_buf[LABEL_INDEX_LEN_CRC_SIZE];

    u32 label_addr = vm_area_p->addr + VM_MAGIC_SIZE;
    u32 test_addr;
    u32 offset_addr = vm_area_p->addr;
    u32 magic_addr_end = label_addr + vm_area_p->len;

    _vm_len vm_len;
    _vm_idx vm_idx;
    _vm_crc vm_crc;
    _vm_crc crc_c;

    while (1) {
        sfc_read(data_buf, label_addr, LABEL_INDEX_LEN_CRC_SIZE);
        vm_put_buf(data_buf, LABEL_INDEX_LEN_CRC_SIZE);

        vm_len = VM_UNIT_LEN(data_buf);
        vm_idx = VM_UNIT_IDX(data_buf);
        vm_crc = VM_UNIT_CRC(data_buf);

        crc_c = (_vm_crc)CRC16((const void *)SPI_CACHE_READ_ADDR(label_addr + LABEL_INDEX_LEN_CRC_SIZE), vm_len);

        if (vm_crc == crc_c) {
            VM_IDX_ADR(vm_idx) = label_addr - offset_addr;

            label_addr += LABEL_INDEX_LEN_CRC_SIZE + vm_len;

            if (label_addr + LABEL_INDEX_LEN_CRC_SIZE >= magic_addr_end) {
                vm_puts("vm_find_the_end_of_vm_area\n");
                break;
            }
        } else {    // crc_err
            vm_puts("vm_find_all_index_last_updated_addr CRC_ERROR, maybe power down cause.\n");
            vm_info_log("vm_crc = 0x%x\n", vm_crc);
            vm_info_log("crc_c = 0x%x\n", crc_c);
            break;
        }
    }

    return label_addr;
}

/**@brief  VM整理函数
  @param  void
  @return 错误返回
  @note   整理后会切换使用区域，内部调用，整理过程需要考虑断电情况
 */
static VM_ERR _vm_defrag(u8 level)
{
    u8 flag_buf[LABEL_INDEX_LEN_CRC_SIZE];
    u32 vm_magic;
    vm_hdl hdl;
    u16 len;

    vm_info_log("\n\n\n\n\n\n\n\n\n\n\n\n------vm_defrag------\n");

    // ----------------check_befor_defrag---------------------------------- //
    if (VM_AREA_ERASE_INFO(VM_AREA_UNSUED)) { // area_need_erased
        vm_puts("unused_area_need_erase_fisrt\n");
        return VM_DEFRAG_ERR;
    }

    vm_info_log("--func = %s--\n", __FUNCTION__);

    // ----------------start_defrag---------------------------------- //
    VM_LAST_ADR = VM_AREA_ADR(VM_AREA_UNSUED) + VM_MAGIC_SIZE;

    vm_magic = VM_MAGIC_DEFRAGGING;
    sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_UNSUED), VM_MAGIC_SIZE);

    for (hdl = 0; hdl < VM_INDEX_DES_MAX; hdl++) {

#if VM_WRITE_RAM_WHEN_FULL
        VM_WRITE_RAM_LIST *ram_space;
        ram_space = vm_write_ram_list_check(hdl);
        if (ram_space) {    // clean_data in list
            len = VM_UNIT_LEN(ram_space->magic_unit);

            vm_puts("\n vm_write_ram_list_check\n");
            vm_info_log("hdl = %d\n", hdl);
            vm_info_log("len = %x\n", len);
            vm_info_log("VM_LAST_ADR = %x\n", VM_LAST_ADR);
            vm_put_buf(ram_space->magic_unit, len);

            if (VM_LAST_ADR + LABEL_INDEX_LEN_CRC_SIZE + len > VM_AREA_ADR(VM_AREA_UNSUED) +
                VM_AREA_LEN(VM_AREA_UNSUED)) {
                vm_err_log("\n vmm_remove_last_updated_types-VM_WRITE_OVERFLOW---ERROR2\n");
                vm_status(&vm_obj);
                vm_reset();
                return VM_WRITE_OVERFLOW;
            }

            sfc_write(ram_space->magic_unit, VM_LAST_ADR, LABEL_INDEX_LEN_CRC_SIZE);
            sfc_write(ram_space->data, VM_LAST_ADR + LABEL_INDEX_LEN_CRC_SIZE, len);
            VM_IDX_ADR(hdl) = VM_LAST_ADR - VM_AREA_ADR(VM_AREA_UNSUED);
            VM_LAST_ADR += (LABEL_INDEX_LEN_CRC_SIZE + len);

            list_del(&ram_space->list);
            free(ram_space);
        } else
#endif
        {
            if (VM_IDX_ADR(hdl)) { // clean_data_in_flash
                sfc_read(flag_buf, VM_IDX_ADR(hdl) + VM_AREA_ADR(VM_AREA_USING), LABEL_INDEX_LEN_CRC_SIZE);
                len = VM_UNIT_LEN(flag_buf);

                if (VM_LAST_ADR + LABEL_INDEX_LEN_CRC_SIZE + len > VM_AREA_ADR(VM_AREA_UNSUED) +
                    VM_AREA_LEN(VM_AREA_UNSUED)) {
                    vm_err_log("\n vmm_remove_last_updated_types-VM_WRITE_OVERFLOW---ERROR2\n");
                    vm_status(&vm_obj);
                    vm_reset();
                    return VM_WRITE_OVERFLOW;
                }

                vm_puts("vm_data_copy\n");
                vm_info_log("hdl = %d\n", hdl);
                vm_info_log("VM_LAST_ADR = 0x%x\n", VM_LAST_ADR);
                vm_info_log("VM_IDX_ADR(hdl) = 0x%x\n", VM_IDX_ADR(hdl));
                vm_put_buf((void *)SPI_CACHE_READ_ADDR(VM_AREA_ADR(VM_AREA_USING) + VM_IDX_ADR(hdl)),
                    LABEL_INDEX_LEN_CRC_SIZE + len);

                vm_data_copy(VM_LAST_ADR, VM_AREA_ADR(VM_AREA_USING) + VM_IDX_ADR(hdl), LABEL_INDEX_LEN_CRC_SIZE + len);
                VM_IDX_ADR(hdl) = VM_LAST_ADR - VM_AREA_ADR(VM_AREA_UNSUED);
                VM_LAST_ADR += (LABEL_INDEX_LEN_CRC_SIZE + len);
            } else {
                vm_info_log("hdl = %d is NOT used\n", hdl);
            }
        }
    }

    vm_puts("\n--------vm_data_copy_over---------\n");

    vm_puts("\n defrag_over_resume_magic\n");
    // ----------------defrag_over_resume_magic---------------------------------- //
    vm_magic = VM_MAGIC_DEFRAG_OVER;
    sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_UNSUED), VM_MAGIC_SIZE);

    if (level) {
        _vm_area_erase(&vm_obj.area[VM_AREA_USING]);    // this function will wast a log time to erase vm_erea_using
    } else {
        vm_magic = VM_MAGIC_WAIT_ERASE;
        sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_USING), VM_MAGIC_SIZE);
        if (VM_PAGE_MODE) {
            // all area_need_erase
            VM_AREA_ERASE_INFO(VM_AREA_USING) = (u16)(0xffff >> (16 - (VM_AREA_LEN(VM_AREA_UNSUED) / VM_PAGE_SIZE)));
        } else {
            // all area_need_erase
            VM_AREA_ERASE_INFO(VM_AREA_USING) = (u16)(0xffff >> (16 - (VM_AREA_LEN(VM_AREA_UNSUED) / VM_SECTOR_SIZE)));
        }
        vm_info_log("---VM_AREA_ERASE_INFO(VM_AREA_USING) = %x---\n", VM_AREA_ERASE_INFO(VM_AREA_USING));
    }

    // switch vm_area
    VM_AREA_USING = VM_AREA_UNSUED;

    vm_magic = VM_MAGIC_USING;
    sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_USING), VM_MAGIC_SIZE);

    // ----------------defrag_finish---------------------------------- //
    vm_info_log("---VM_AREA_USING= %d---\n", VM_AREA_USING);

    return VM_ERR_NONE;
}

/**@brief  VM区域检测函数
  @param  vm_area_p：VM区域，addr：要检查的地址：len：要检查的长度
  @return 错误返回
  @note   保证区域干净
 */
static int vm_area_check(AREA_INFO *vm_area_p, u32 addr, u32 len)
{
    if (vm_bytecmp(SPI_CACHE_READ_ADDR(addr), 0xff, len)) {
        vm_info_log("---addr = 0x%x---\n", addr);
        return FALSE; // area have some data leftover
    } else {
        return TRUE; // all area is clean
    }
}

/**@brief  VM_WARNING_LINE检测函数
  @param  void：默认检测单前使用区域
  @return FALSE：未到达警告线，TRUE：以到达警告
  @note   用于整理VM前检测
 */
static BOOL vm_warning_line_check(void)
{
    u32 warning_adr = VM_LINE * VM_AREA_LEN(VM_AREA_USING);
    warning_adr /= 100;

    vm_info_log("VM_LAST_ADR = 0x%x     ", VM_LAST_ADR);
    vm_info_log("VM_AREA_ADR(VM_AREA_USING)= 0x%x       ", VM_AREA_ADR(VM_AREA_USING));
    vm_info_log("warning_adr = 0x%x\n", warning_adr);

    vm_info_log("VM %d %% full\n", (VM_LAST_ADR - VM_AREA_ADR(VM_AREA_USING)) * 100 / VM_AREA_LEN(VM_AREA_USING));

    if (VM_LAST_ADR > (VM_AREA_ADR(VM_AREA_USING) + warning_adr)) {
        return TRUE; // area used arrived to warning line and need to be erase
    } else {
        return FALSE; // area have enought sapce
    }
}

/**@brief  VM_WARNING_LINE检测函数
  @param  void：默认检测单前使用区域
  @return FALSE：未到达警告线，TRUE：以到达警告
  @note   用于整理VM前检测
 */
static VM_ERR vm_reset(void)
{
    vm_info_log("--func = %s--\n", __FUNCTION__);

    u32 vm_magic = VM_MAGIC_USING;

    _vm_area_erase(&vm_obj.area[0]);
    _vm_area_erase(&vm_obj.area[1]);

    VM_AREA_USING = 0;

    sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_USING), VM_MAGIC_SIZE);
    VM_LAST_ADR = VM_AREA_ADR(VM_AREA_USING) + VM_MAGIC_SIZE;

    memset_s(vm_obj.idx_desc, sizeof(VM_DESC) * VM_INDEX_DES_MAX, 0x00, sizeof(VM_DESC) * VM_INDEX_DES_MAX);

    return VM_ERR_NONE;
}

/**@brief  VM初始化函数，所有VM操作之前都必须先初始化
  @param  vm_addr：VM起始地址
  @param  vm_len： VM总大小空间
  @param  vm_mode：是否是能中断
  @param  *path：升级文件名
  @param  reserve_area：升级保留区大小
  @return none
  @note   VM_ERR vm_init(u32 vm_addr, u32 vm_len, u8 vm_mode)
 */
#define AREA_A  0
#define AREA_B  1
VM_ERR vm_init(void *dev_hdl, u32 vm_addr, u32 vm_len, u8 vm_mode /* , const char *path, u16 reserve_area */)
{
    VM_ERR ret = VM_ERR_NONE;

    u32 vm_magic;
    u32 vm_magic_a;
    u32 vm_magic_b;
    u32 remove_len;

    vm_info_log("--func = %s--\n", __FUNCTION__);

    if (vm_init_check() == TRUE) { // already_init
        vm_err_log("vm_init_err2\n");
        return VM_INIT_ALREADY;
    }

    ret = set_protect_info(vm_addr, vm_len);
    if (VM_ERR_NONE != ret) {
        vm_err_log("vm_init_err1\n");
        return ret;
    }

    if (vm_len < (2 * VM_PAGE_SIZE)) {
        return VM_ERR_INIT;
    }

    VM_PAGE_MODE = vm_mode;

    vm_info_log("--vm_adr = 0x%x--\n", vm_addr);
    vm_info_log("--vm_len = 0x%x--\n", vm_len);
    vm_info_log("--vm_mode = 0x%x--\n", vm_mode);

    u32 vm_addr_ali, vm_len_ali;
    if ((VM_PAGE_MODE == 1) && (vm_len >= (4 * VM_SECTOR_SIZE))) {
        // 4 * sector 切换到 sector_mode
        VM_PAGE_MODE = 0;
    }

    if (VM_PAGE_MODE == 0) {
        vm_addr_ali = (u32)VM_SECTOR_ALIGN_BACKWARD(vm_addr + VM_SECTOR_SIZE - 1);
        vm_len_ali  = vm_len - (vm_addr_ali - vm_addr);
        vm_len_ali  = vm_len_ali / (VM_SECTOR_SIZE * 2) * (VM_SECTOR_SIZE * 2);
        vm_addr = vm_addr_ali;
        vm_len  = vm_len_ali;
        if (vm_len > VM_MAX_SECTOR_SIZE) {
            vm_len = VM_MAX_SECTOR_SIZE;
        }
    } else {
        vm_addr_ali = (u32)VM_PAGE_ALIGN_BACKWARD(vm_addr + VM_PAGE_SIZE - 1);
        vm_len_ali  = vm_len - (vm_addr_ali - vm_addr);
        vm_len_ali  = vm_len_ali / (VM_PAGE_SIZE * 2) * (VM_PAGE_SIZE * 2);
        vm_addr = vm_addr_ali;
        vm_len  = vm_len_ali;
        if (vm_len > VM_MAX_PAGE_SIZE) {
            vm_len = VM_MAX_PAGE_SIZE;
        }
    }

    vm_info_log("--vm_adr_ali = 0x%x--\n", vm_addr);
    vm_info_log("--vm_len_ali = 0x%x--\n", vm_len);
    vm_info_log("--vm_mode = 0x%x--\n",    VM_PAGE_MODE);

    // all_vm_info_check_ok
    memset_s(vm_obj.idx_desc, sizeof(VM_DESC) * VM_INDEX_DES_MAX, 0x00, sizeof(VM_DESC) * VM_INDEX_DES_MAX);

    vm_len >>= 1;
    VM_AREA_LEN(AREA_A) = vm_len;
    VM_AREA_ADR(AREA_A) = vm_addr;
    VM_AREA_LEN(AREA_B) = vm_len;
    VM_AREA_ADR(AREA_B) = vm_addr + vm_len;
    VM_LINE = VM_DEFRAG_SIZE;

#if VM_WRITE_RAM_WHEN_FULL
    INIT_LIST_HEAD(&VM_WRITE_LIST);
#endif

    vm_info_log("vm_area_a adr = 0x%x\n", VM_AREA_ADR(AREA_A));
    vm_info_log("vm_area_a len = 0x%x\n", VM_AREA_LEN(AREA_A));
    vm_info_log("vm_area_b adr = 0x%x\n", VM_AREA_ADR(AREA_B));
    vm_info_log("vm_area_b len = 0x%x\n", VM_AREA_LEN(AREA_B));

    sfc_read((u8 *)&vm_magic_a, VM_AREA_ADR(AREA_A), VM_MAGIC_SIZE);
    sfc_read((u8 *)&vm_magic_b, VM_AREA_ADR(AREA_B), VM_MAGIC_SIZE);
    vm_info_log("vm_area_a magic = 0x%x\n", vm_magic_a);
    vm_info_log("vm_area_b magic = 0x%x\n", vm_magic_b);

    switch (vm_magic_a) {
        case VM_MAGIC_USING: // area_a_data is complete
            if (vm_magic_b == VM_MAGIC_DEFRAG_OVER) {
                vm_magic = VM_AREA_USING;
                sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(AREA_B), VM_MAGIC_SIZE);
                VM_AREA_USING = AREA_B;
            } else {
                VM_AREA_USING = AREA_A;
            }
            break;

        case VM_MAGIC_DEFRAG_OVER: // area_a_data is complete
            // set_area_a as using
            vm_magic = VM_AREA_USING;
            sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(AREA_A), VM_MAGIC_SIZE);
            VM_AREA_USING = AREA_A;
            break;

        case VM_MAGIC_UNUSED:
        case VM_MAGIC_DEFRAGGING:
        case VM_MAGIC_WAIT_ERASE:
            if (vm_magic_b == VM_MAGIC_USING) {
                VM_AREA_USING = AREA_B;
            } else if (vm_magic_b == VM_MAGIC_UNUSED) {
                vm_puts("---vm_area_all_unused---\nvm_eraser\n");
                vm_reset();
            } else {
                vm_puts("VM_MAGIC_ERR_A-1\n");
                vm_reset();
            }
            break;
        default:
            vm_puts("---vm_area_all_err---\nvm_eraser\n");
            vm_reset();
            break;
    }
    vm_info_log("---VM_AREA_USING  = %d---\n", VM_AREA_USING);

    // check_unused_area_first
    if (vm_area_check(&vm_obj.area[VM_AREA_UNSUED], VM_AREA_ADR(VM_AREA_UNSUED), VM_AREA_LEN(VM_AREA_UNSUED)) ==
        FALSE) {
        vm_puts("vm_check_erase_err:unused\n");
        _vm_area_erase(&vm_obj.area[VM_AREA_UNSUED]);
    }

    // check_using_area_last
    VM_LAST_ADR = vm_renew_all_index_last_updated_addr(&vm_obj.area[VM_AREA_USING]);

    if (vm_area_check(&vm_obj.area[VM_AREA_USING], VM_LAST_ADR, VM_AREA_ADR(VM_AREA_USING) +
        VM_AREA_LEN(VM_AREA_USING) - VM_LAST_ADR) == FALSE) {
        vm_puts("vm_check_erase_err:using\n");
        _vm_defrag(1);
    }
    vm_info_log("---VM_LAST_ADR= 0x%x---\n", VM_LAST_ADR);

    if (vm_warning_line_check()) {
        vm_puts("vm_warning_line_check:arrived_warning_line\n");
        _vm_defrag(1);
    }

    vm_mutex_init();

    VM_INIT_FLAG = 1;

    return ret;
}

/*----------------------------------------------------------------------------*/
/**@brief  VM区域擦除函数
  @param  void
  @return none
  @note   VM_ERR vm_eraser(void)
 */
/*----------------------------------------------------------------------------*/
VM_ERR vm_eraser(void)
{
    if (vm_init_check() == FALSE) {
        return VM_NOT_INIT;
    }

    vm_mutex_enter();

    u32 vm_magic = VM_MAGIC_USING;

    _vm_area_erase(&vm_obj.area[0]);
    _vm_area_erase(&vm_obj.area[1]);

    VM_AREA_USING = 0;

    sfc_write((const u8 *)&vm_magic, VM_AREA_ADR(VM_AREA_USING), VM_MAGIC_SIZE);
    VM_LAST_ADR = VM_AREA_ADR(VM_AREA_USING) + VM_MAGIC_SIZE;

    memset_s(vm_obj.idx_desc, sizeof(VM_DESC) * VM_INDEX_DES_MAX, 0x00, sizeof(VM_DESC) * VM_INDEX_DES_MAX);

    vm_mutex_exit();

    return VM_ERR_NONE;
}

/*----------------------------------------------------------------------------*/
/**@brief  VM申请
  @param  index：vm申请序号
  @return handle
  @note   s32 vm_open(u8 index)
 */
/*----------------------------------------------------------------------------*/
s32 vm_open(u16 index)
{
    vm_hdl hdl;

    if (vm_check_hdl(index) == FALSE) {
        return VM_INDEX_ERR;
    }

    vm_mutex_enter();

    hdl = index;

    vm_mutex_exit();
    return hdl;
}

/**@brief  VM关闭
  @param  index：vm申请序号
  @return handle
  @note   VM_ERR vm_close(vm_hdl hdl)
 */
VM_ERR vm_close(vm_hdl hdl)
{
    if (vm_check_hdl(hdl) == FALSE) {
        return VM_INDEX_ERR;
    }
    vm_mutex_enter();

    vm_mutex_exit();
    return VM_ERR_NONE;
}

s32 _vm_write(vm_hdl hdl, u8 *data_buf, u16 len, u8 dma_mode)
{
    s32 ret;
    u8 flag_buf[LABEL_INDEX_LEN_CRC_SIZE];

    if (vm_init_check() == FALSE) {
        return VM_NOT_INIT;
    }

    if (vm_check_hdl(hdl) == FALSE) {
        return VM_INDEX_ERR;
    }

    if (len > VM_MAX_DATA_LEN) {
        vm_err_log("write len err : len = %d\n", len);
        return VM_DATA_LEN_ERR;
    }

    vm_check_all(1);

#if 1
    u8 *read_buf = malloc(len);
    u16 read_len;
    if (read_buf) {
        read_len = vm_read(hdl, read_buf, len);
        if (read_len == len) {
            if (memcmp(read_buf, data_buf, len) == 0) {
                /* the same context */
                free(read_buf);
                return len;
            }
        }
        free(read_buf);
    }
#endif

    vm_mutex_enter();

#if 1
    if (LABEL_INDEX_LEN_CRC_SIZE + len > VM_AREA_ADR(VM_AREA_USING) + VM_AREA_LEN(VM_AREA_USING) - VM_LAST_ADR) {
        vm_puts("vm space need defrag \n");
        _vm_defrag(1);
    }
#endif

    _vm_len vm_len = len;
    _vm_crc data_crc = (_vm_crc)CRC16(data_buf, vm_len);

    flag_buf[0] = data_crc;;
    flag_buf[1] = (u8)(hdl & 0xFF);
    flag_buf[2] = (u8)(((vm_len & 0x0F) << 4) | ((hdl & 0x0F00) >> 8));
    flag_buf[3] = (u8)(vm_len >> 4);

    ASSERT(VM_LAST_ADR > VM_AREA_ADR(VM_AREA_USING), "0x%x, 0x%x, 0x%x", VM_LAST_ADR > VM_AREA_ADR(VM_AREA_USING),
        VM_AREA_LEN(VM_AREA_USING));
    ASSERT(VM_LAST_ADR - VM_AREA_ADR(VM_AREA_USING) < VM_AREA_LEN(VM_AREA_USING), "0x%x, 0x%x, 0x%x",
        VM_LAST_ADR > VM_AREA_ADR(VM_AREA_USING), VM_AREA_LEN(VM_AREA_USING));

    if (LABEL_INDEX_LEN_CRC_SIZE + vm_len <= VM_AREA_ADR(VM_AREA_USING) + VM_AREA_LEN(VM_AREA_USING) - VM_LAST_ADR) {
        sfc_write(flag_buf, VM_LAST_ADR, LABEL_INDEX_LEN_CRC_SIZE);
        if (vm_len) {
            if (dma_mode) {
                sfc_dma_write(data_buf, VM_LAST_ADR + LABEL_INDEX_LEN_CRC_SIZE, vm_len);
            } else {
                sfc_write(data_buf, VM_LAST_ADR + LABEL_INDEX_LEN_CRC_SIZE, vm_len);
            }
        }
        VM_IDX_ADR(hdl) = VM_LAST_ADR - VM_AREA_ADR(VM_AREA_USING);

        vm_info_log("vm_write idx:%d, idx_adr:0x%x, idx_len:0x%x\n", hdl, VM_IDX_ADR(hdl), vm_len +
            LABEL_INDEX_LEN_CRC_SIZE);
        vm_put_buf((void *)flag_buf, LABEL_INDEX_LEN_CRC_SIZE);
        vm_put_buf((void *)data_buf, vm_len);

        VM_LAST_ADR += LABEL_INDEX_LEN_CRC_SIZE + vm_len;
        ret = vm_len;
    } else { // block overflow
#if VM_WRITE_RAM_WHEN_FULL
        VM_WRITE_RAM_LIST *ram_space;
        ram_space = vm_write_ram_list_check(hdl);

        if (ram_space) {
            if (ram_space->len < len) {
                list_del(&ram_space->list);
                free(ram_space);
                ram_space = NULL;
            }
        }

        if (ram_space == NULL) {
            vm_info_log("ram_space_malloc = %d\n", sizeof(VM_WRITE_RAM_LIST) + vm_len);
            ram_space = malloc(sizeof(VM_WRITE_RAM_LIST) + vm_len);
            if (!ram_space) {
                vm_puts("vm_write_ram_malloc_err\n");
                ret = VM_WRITE_OVERFLOW;
                goto EXIT;
            }
            memset_s(ram_space, sizeof(VM_WRITE_RAM_LIST) + vm_len, 0x00, sizeof(VM_WRITE_RAM_LIST) + vm_len);
            list_add(&ram_space->list, &VM_WRITE_LIST);
            ram_space->len = len;
        }

        memcpy_s(ram_space->magic_unit, LABEL_INDEX_LEN_CRC_SIZE, flag_buf, LABEL_INDEX_LEN_CRC_SIZE);
        memcpy_s(ram_space->data, vm_len, data_buf, vm_len);
        vm_puts("vm_write_ram_buf:\n");
        vm_put_buf(ram_space->magic_unit, LABEL_INDEX_LEN_CRC_SIZE + vm_len);

        ret = vm_len;
#else
        vm_err_log("block write overflow:\n");
        ret = VM_WRITE_OVERFLOW;
        goto EXIT;
#endif
    }

EXIT:
    vm_mutex_exit();

    return ret;
}

s32 vm_write(vm_hdl hdl, u8 *data_buf, u16 len)
{
    return _vm_write(hdl, data_buf, len, 0);
}

s32 vm_dma_write(vm_hdl hdl, u8 *data_buf, u16 len)
{
    return _vm_write(hdl, data_buf, len, 1);
}

s32 vm_read(vm_hdl hdl, u8 *data_buf, u16 len)
{
    s32 ret;
    _vm_len vm_len;
    _vm_idx vm_idx;
    _vm_crc vm_crc;
    _vm_crc crc_c;

    u8 flag_buf[LABEL_INDEX_LEN_CRC_SIZE];

    if (vm_init_check() == FALSE) {
        vm_err_log("VM_NOT_INIT\n");
        return VM_NOT_INIT;
    }

    if (vm_check_hdl(hdl) == FALSE) {
        vm_err_log("VM_INDEX_ERR\n");
        return VM_INDEX_ERR;
    }

    if (len == 0) {
        return 0;
    }

    vm_mutex_enter();

#if VM_WRITE_RAM_WHEN_FULL
    VM_WRITE_RAM_LIST *ram_space;
    ram_space = vm_write_ram_list_check(hdl);       // clean_data_in_list
    if (ram_space) { // clean_data_in_list

        vm_puts("read_from_ram\n");

        vm_len = VM_UNIT_LEN(ram_space->magic_unit);
        vm_crc = VM_UNIT_CRC(ram_space->magic_unit);

        crc_c = (_vm_crc)CRC16((const void *)ram_space->data, vm_len);

        if (vm_crc == crc_c) {
            if (vm_len > len) {
                vm_len = len;
            }
            ret = vm_len;
            memcpy_s(data_buf, vm_len, ram_space->data, vm_len);
        } else {
            vm_err_log("read_from_ram err\n");
            ret = VM_READ_DATA_ERR;
            goto EXIT;
        }
    } else
#endif
    {
        if (VM_IDX_ADR(hdl)) {
            vm_puts("read_from_flash\n");
            sfc_read(flag_buf, VM_AREA_ADR(VM_AREA_USING) + VM_IDX_ADR(hdl), LABEL_INDEX_LEN_CRC_SIZE);

            vm_len = VM_UNIT_LEN(flag_buf);
            vm_crc = VM_UNIT_CRC(flag_buf);

            crc_c = (_vm_crc)CRC16((const void *)SPI_CACHE_READ_ADDR(VM_AREA_ADR(VM_AREA_USING) + VM_IDX_ADR(hdl) +
                LABEL_INDEX_LEN_CRC_SIZE), vm_len);

            if (vm_crc == crc_c) {
                if (vm_len > len) {
                    vm_len = len;
                }
                ret = vm_len;
                sfc_read(data_buf, VM_AREA_ADR(VM_AREA_USING) + LABEL_INDEX_LEN_CRC_SIZE + VM_IDX_ADR(hdl), vm_len);
                goto EXIT;
            } else {
                vm_err_log("read_from_vm err, hdl = %d\n", hdl);
                vm_err_log("vm_crc:0x%x crc_c:0x%x\n", vm_crc, crc_c);
                ret = VM_READ_DATA_ERR;
                goto EXIT;
            }
        } else {
            vm_err_log("nodata in vm, hdl = %d\n", hdl);
            ret = VM_READ_DATA_ERR;
            goto EXIT;
        }
    }
EXIT:
    vm_mutex_exit();
    return ret;
}

VM_ERR vm_status(VM_STRUCT *vm_str_p)
{
    vm_info_log("---VM_AREA_USINGE = %d---\n", VM_AREA_USING);
    vm_info_log("---VM_AREA_ADR = 0x%x---\n", VM_AREA_ADR(VM_AREA_USING));
    vm_info_log("---VM_AREA_ADR = 0x%x---\n", VM_AREA_LEN(VM_AREA_USING));

    if (!vm_str_p) {
        return VM_READ_DATA_ERR;
    }
    if (vm_str_p != &vm_obj) {
        memcpy_s(vm_str_p, sizeof(VM_STRUCT), &vm_obj, sizeof(VM_STRUCT));
    }

    return VM_ERR_NONE;
}

// level: 0:erase_and_return, 1:erase_all_area
// return : not clean num
static VM_ERR vm_erase_check(u8 level)
{
    u8 bit_cnt;
    u8 bit_size;
    u8 need_clean_num;

    u8 find_flag = 0;

    if (vm_init_check() == FALSE) {
        return VM_NOT_INIT;
    }

    vm_mutex_enter();

    if (level) { // erase_vm_area
        if (VM_AREA_ERASE_INFO(VM_AREA_UNSUED)) { // area_need_erased
            _vm_area_erase(&vm_obj.area[VM_AREA_UNSUED]);
        }
        need_clean_num = 0;
        goto EXIT;
    } else {    // find 1 sector and erase
        if (VM_AREA_ERASE_INFO(VM_AREA_UNSUED)) { // area_need_erased
            need_clean_num = 0;

            if (VM_PAGE_MODE) {
                bit_size = VM_AREA_LEN(VM_AREA_UNSUED) / VM_PAGE_SIZE;
            } else {
                bit_size = VM_AREA_LEN(VM_AREA_UNSUED) / VM_SECTOR_SIZE;
            }

            for (bit_cnt = 0; bit_cnt < bit_size; bit_cnt++) {
                if (VM_AREA_ERASE_INFO(VM_AREA_UNSUED) & BIT(bit_cnt)) {
                    if (find_flag == 0) { // find it
                        find_flag = 1;
                        if (VM_PAGE_MODE) {
                            if (sfc_erase(PAGE_ERASER, VM_AREA_ADR(VM_AREA_UNSUED) + VM_PAGE_SIZE * bit_cnt) == true) {
                                VM_AREA_ERASE_INFO(VM_AREA_UNSUED) &= ~BIT(bit_cnt);
                            } else {
                                vm_err_log("\n--------page_erase_err---------\n");
                            }
                        } else {
                            if (sfc_erase(SECTOR_ERASER, VM_AREA_ADR(VM_AREA_UNSUED) + VM_SECTOR_SIZE * bit_cnt) ==
                                true) {
                                VM_AREA_ERASE_INFO(VM_AREA_UNSUED) &= ~BIT(bit_cnt);
                            } else {
                                vm_err_log("\n--------sector_erase_err---------\n");
                            }
                        }
                    } else {
                        need_clean_num++;   // count_sector_unclean_in_area
                    }
                }
            }
        } else {
            need_clean_num = 0;
        }
    }

EXIT:
    vm_mutex_exit();
    return need_clean_num;
}

/**@brief  VM整理函数检测函数，检测是否需要defrag
  @param  level：0：低优先级整理，以最快速度整理VM区域，1：高优先级处理VM区域
  @return NON
  @note   static VM_ERR vm_defrag_check(u8 level)
 */
static VM_ERR vm_defrag_check(u8 level)
{
    VM_ERR ret = VM_ERR_NONE;

    if (vm_init_check() == FALSE) {
        return VM_NOT_INIT;
    }

    vm_mutex_enter();
    if (vm_warning_line_check() == TRUE) { // need to be defraged
        ret = _vm_defrag(level);
    }

#if VM_WRITE_RAM_WHEN_FULL
    if (!list_empty(&VM_WRITE_LIST)) {  // some clean data in list
        ret = _vm_defrag(level);
    }
#endif

    vm_mutex_exit();

    return ret;
}

/**@brief  VM检测函数
  @param  level：0：以最快速度整理VM区域，但不一定全区域整理，1：整理全VM区域
  @return NON
  @note   外部必须定时通过此接口查询VM是否需要整理。
 */
void vm_check_all(u8 level)
{
    s32 ret;

    ret = vm_erase_check(level);

    if (ret == 0 || level) { // all unused atea is clean
        ret = vm_defrag_check(level);
    }
}

// -------------------------flash_protect_function------------------//
static VM_ERR set_protect_info(u32 addr, u32 len)
{
#if VM_PROTECT_CHECK_EN
    vm_obj.vm_protect.addr = addr;
    vm_obj.vm_protect.len = len;
#endif

    return VM_ERR_NONE;
}

/**@brief  内部flash
  @param  addr：擦地址，flash相对地址
  @return VM_ERR_NONE：检查通过，other：区域禁止操作
  @note   static VM_ERR protect_check(u32 addr , u32 len)
 */
static VM_ERR protect_check(u32 addr, u32 len)
{
#if VM_PROTECT_CHECK_EN
    if ((addr < vm_obj.vm_protect.addr) || ((addr + len) > (vm_obj.vm_protect.addr + vm_obj.vm_protect.len))) {
        return VM_ERR_PROTECT;
    }
#endif

    return VM_ERR_NONE;
}

/**@brief  内部flash
  @param  cmd：擦方式
  @param  addr：擦地址，flash相对地址
  @return 0：擦错误，1：擦成功
  @note   u32 sfc_read(u8 *buf, u32 addr, u32 len)
 */
BOOL sfc_erase(FLASH_ERASER cmd, u32 addr)
{
    u32 dev_cmd;
    VM_ERR ret = VM_ERR_NONE;
    /*
       -----protect-code-----
     */
    if (cmd == BLOCK_ERASER) {
        dev_cmd = IOCTL_ERASE_BLOCK;
        addr = (u32)VM_BLOCK_ALIGN_BACKWARD(addr);
        ret = protect_check(addr, VM_BLOCK_SIZE);
    } else if (cmd == SECTOR_ERASER) {
        dev_cmd = IOCTL_ERASE_SECTOR;
        addr = (u32)VM_SECTOR_ALIGN_BACKWARD(addr);
        ret = protect_check(addr, VM_SECTOR_SIZE);
    } else if (cmd == PAGE_ERASER) {
        dev_cmd = IOCTL_ERASE_PAGE;
        addr = (u32)VM_PAGE_ALIGN_BACKWARD(addr);
        ret = protect_check(addr, VM_PAGE_SIZE);
    } else {
        return false;
    }

    if (ret != VM_ERR_NONE) {
        return false;
    }

    norflash_ioctl(NULL, dev_cmd, addr);

    return true;
}

BOOL sfc_erase_zone(u32 addr, u32 len)
{
    FLASH_ERASER cmd;
    u32 erase_unit;
    if (VM_PAGE_MODE) {
        cmd = PAGE_ERASER;
        erase_unit = 256;
    } else {
        cmd = SECTOR_ERASER;
        erase_unit = 4096;
    }

    const u32 end_addr = addr + len;
    for (int i = addr; i < end_addr; i += erase_unit) {
        if (sfc_erase(cmd, i) == 0) {
            return FALSE;
        }
    }
    return true;
}

/**@brief  内部flash
  @param  buf：写内容
  @param  addr：写地址，flash相对地址
  @param  len：写长度，单位byte
  @return 0：写错误，1：写成功len
  @note   u32 sfc_read(u8 *buf, u32 addr, u32 len)
 */
u32 sfc_write(const u8 *buf, u32 addr, u32 len)
{
    VM_ERR ret = VM_ERR_NONE;
    /*
       -----protect-code-----
     */
    ret = protect_check(addr, len);
    if (ret != VM_ERR_NONE) {
        return false;
    }

    norflash_write(NULL, (void *)buf, len, addr);

    return len;
}

/**@brief  内部flash, dma mode
  @param  buf：写内容
  @param  addr：写地址，flash相对地址
  @param  len：写长度，单位byte
  @return 0：写错误，1：写成功len
  @note   u32 sfc_read(u8 *buf, u32 addr, u32 len)
 */
u32 sfc_dma_write(const u8 *buf, u32 addr, u32 len)
{
    VM_ERR ret = VM_ERR_NONE;
    /*
       -----protect-code-----
     */
    ret = protect_check(addr, len);
    if (ret != VM_ERR_NONE) {
        return false;
    }

    norflash_dma_write(NULL, (void *)buf, len, addr);

    return len;
}

/**@brief  内部flash
  @param  buf：读内容
  @param  addr：读地址，flash相对地址
  @param  len：读长度，单位byte
  @return 0：读错误，1：读成功
  @note   u32 sfc_read(u8 *buf, u32 addr, u32 len)
 */
u32 sfc_read(u8 *buf, u32 addr, u32 len)
{
    VM_ERR ret = VM_ERR_NONE;

    norflash_read(NULL, (void *)buf, len, addr);
    return len;
}

/* --debug_func-- */
void vm_test(void)
{
}

